Custom Directive (graphql)
自分でSchema Directives (graphql)を定義するもの
よくある例
table:_
@auth(role: "ADMIN") 認可(Authorization)
@upper 文字列を大文字に変換
@formatDate(pattern: "...") 日付の整形
@computed / @derived 計算フィールドを示す
@cacheControl キャッシュ戦略
定義する流れ
① GraphQL SDL に宣言する
directive @xxx on ...)という形式
e.g. 認可用に@auth を作りたいとする
code:graphql(js)
enum Role {
USER
ADMIN
}
directive @auth(role: Role!) on FIELD_DEFINITION
directive @auth(...) が 自前directiveの宣言
on FIELD_DEFINITION は「フィールド定義にだけ付けられる」という意味
他に OBJECT, SCALAR, ENUM, INPUT_FIELD_DEFINITION なども指定可能
② スキーマ内で実際に使う
code:graphql(js)
type Query {
me: User! @auth(role: USER)
adminDashboard: AdminDashboard! @auth(role: ADMIN)
}
これでメタ情報がスキーマに載る
③ resolverでdirectiveの挙動を実装する
e.g. Apollo Server + graphql-tools で @auth を実装するイメージ
code:ts
import { defaultFieldResolver, GraphQLSchema } from 'graphql';
import { mapSchema, getDirective, MapperKind } from '@graphql-tools/utils';
export function authDirectiveTransformer(schema: GraphQLSchema, directiveName = 'auth') {
return mapSchema(schema, {
MapperKind.OBJECT_FIELD: (fieldConfig) => {
const directives = getDirective(schema, fieldConfig, directiveName);
if (!directives?.length) return fieldConfig;
const { role } = directives0; // @auth(role: ADMIN) の引数
const originalResolve = fieldConfig.resolve ?? defaultFieldResolver;
fieldConfig.resolve = async (source, args, context, info) => {
// ここで context.user などを使って権限チェック
if (!context.user || !context.user.roles.includes(role)) {
throw new Error('Not authorized');
}
return originalResolve(source, args, context, info);
};
return fieldConfig;
},
});
}
これで、@auth が付いているフィールドはすべて権限チェック付きの resolver に変換される